
const NEWLINE = "\r\n";
const SPLIT = " ";
let websocketResole = null;                            // websocket连接Resole
let websocketIsActive = false;

class SignalWebSocket {

    static singleton = null;

    constructor() {
        this.userId = null;
        this.websocket = null;
        this.isActive = false;                                 // 网络连接是否连接中
        this.isReconnection = false;                           // 是否重连

        this.resultMap = new Map();                            // 存储发送消息的回调
        this.callbackMap = new Map();                          // 接受服务器端主动发起的消息
        this.webSocketUrl = undefined;
    }

    static getInstance() {
        if (Objects.isNull(SignalWebSocket.singleton)) {
            console.info("[signal create SignalWebSocket]");
            SignalWebSocket.singleton = new SignalWebSocket();
        }

        return SignalWebSocket.singleton;
    }

    initCallback(callbackFunction) {
        callbackFunction(this.callbackMap);
    }

    async openWebsocket(url) {
        this.webSocketUrl = url;
        this.websocket = new WebSocket(url);
        this.bindEvent();

        return new Promise(function(resole, reject){
            console.info("[signal websocket connecting]", websocketIsActive, websocketResole);
            if (websocketIsActive) {
                resole(true);
                return;
            }
            websocketResole = resole;
            setTimeout(()=>{
                console.info("[signal websocket connecting timeout]", websocketIsActive);
                if (Objects.isNull(websocketResole)) {
                    reject(new TimeoutException('[signal websocket connect]', "连接超时"));
                }
            }, 60 * 1000)
        })
    }

    async sendData(data, timeout)  {
        console.info("[signal webSocket sendData]", data);
        if (!this.isActive) {
            if (Objects.isNotNull(this.userId) && !this.isReconnection) { // 断连
                try {
                    let signalService = new SignalService();
                    this.isReconnection = true
                    await signalService.reconnection();

                    this.isReconnection = false;
                } catch (error) {
                    this.isReconnection = false;
                    console.error("[signal websocket reconnection error]", error.message, error.stack)
                    throw Error(error)
                }
            }
            this.connect();
            console.info("[signal websocket connected]")

        }
        const localSeq = data.extDataMap.get("localSeq");
        let message = this.encodeRpcData(data);
        console.info("[signal websocket send message]", message);
        this.websocket.send(message);
        console.info("[signal websocket send message success]")
        this.lastedWriteDataTime = new Date().getTime();
        return new Promise((resole, reject)=>{
            this.resultMap.set(localSeq, resole);
            if (!(timeout === undefined || timeout <= 0)) {
                setTimeout(()=>{
                    if (this.resultMap.has(localSeq)) {
                        reject(new TimeoutException(data, "获取数据超时"));
                    }
                }, timeout * 1000)
            }
        })
    }

    _sendDataNoResp(data, timeout) {
        let message = this.encodeRpcData(data);
        this.websocket.send(message);
    }

    bindEvent() {
        // 打开事件
        this.websocket.onopen = this.onOpen.bind(this);

        // 接收消息事件
        this.websocket.onmessage = this.onMessage.bind(this);

        // 错误事件
        this.websocket.onerror = this.onError.bind(this);

        // 关闭事件
        this.websocket.onclose = this.onClose.bind(this);
    }

    onOpen(event) {
        this.isActive = true;
        websocketIsActive = true;
        console.info('[signal websocket opened]', this.isActive, websocketResole);
        if (Objects.isNotNull(websocketResole)) {
            websocketResole(true);
        }
        websocketResole = null;
    }

    onMessage(event) {
        console.info("[signal websocket on message]", event.data);
        if (!event.isTrusted) {
            return;
        }

        this.lastedReceiveDataTime = new Date().getTime();
        let data = this.split(event.data);
        let rpcData = this.parseData(data[0], data[1], data[2]);
        let localSeq = rpcData.extDataMap.get("localSeq");
        if (!(localSeq === undefined) && this.resultMap.has(localSeq)) {
            let resole = this.resultMap.get(localSeq);
            if (resole !== undefined) {
                resole(rpcData);
                this.resultMap.delete(localSeq)
                return;
            }
        }

        if (this.callbackMap.has(rpcData.path)) {
            let callback = this.callbackMap.get(rpcData.path);
            if (callback !== undefined) {
                this.resultMap.delete(localSeq)
                let result = callback(rpcData);
                console.info('[signal websocket return data]', result, rpcData);
                if (Objects.isNotNull(result)) {
                    let extDataMap = new Map();
                    extDataMap.set("localSeq", rpcData.extDataMap.get("localSeq"));
                    let data = new RpcData(rpcData.path, rpcData.version, 100, extDataMap, result);
                    this._sendDataNoResp(data, 60);
                    console.info('[signal websocket return data2]', data);
                } else {
                    let extDataMap = new Map();
                    extDataMap.set("localSeq", rpcData.extDataMap.get("localSeq"));
                    let data = new RpcData(rpcData.path, rpcData.version, 100, extDataMap, null);
                    this._sendDataNoResp(data, 60);
                    console.info('[signal websocket return data2]', data);
                }
            }
        }
    }

    onError(event) {
        console.log('[signal websocket error]', event);
    }

    onClose(event) {
        console.log('[signal websocket close]', event);
        this.isActive = false;
        websocketIsActive = false;
    }

    encodeRpcData(rpcData) {
        if (ImSingleton.getInstance().session.has('authCode')) {
            rpcData.extDataMap.set("authCode", ImSingleton.getInstance().session.getData("authCode"));
        }
        let data = JSON.stringify(rpcData.data, this.dateEncode);
        console.info("[signal websocket rpcData]", data.replace(NEWLINE, ","));
        console.info("[signal websocket rpcData extData]", JSON.stringify(rpcData.extDataMap).replace(NEWLINE, ","));
        let head = rpcData.path + SPLIT + rpcData.version.toString() + SPLIT + rpcData.status.toString() + SPLIT + NEWLINE;
        rpcData.extDataMap.forEach((key, value) => {
            head += (value + ":" + key + NEWLINE);
        });
        let body = head + NEWLINE + data;
        console.info("[signal websocket rpcData body]", body.replace(NEWLINE, ",").replace(NEWLINE + NEWLINE, ";"));
        return body;
    }

    connect() {
        if (this.isActive && this.isReconnection) {
            this.websocket.close();
            console.info("[websocket closed]");
        }
        this.openWebsocket(this.webSocketUrl);
    }

    split(content) {
        let idx = content.indexOf(NEWLINE);
        let secondIdx = content.indexOf(NEWLINE + NEWLINE);
        let headerStr = content.substring(0, idx);
        let extData = "";
        if (idx < secondIdx - 2) {
            extData = content.substring(idx+2, secondIdx);
            extData = extData.trim();
        }
        let data = content.substring(secondIdx + 2);
        data = data.trim();
        return [headerStr, extData, data];
    }

    parseData(headStr, extData, data) {
        let headers = headStr.trim().split(SPLIT);
        let path = headers[0];
        let version = parseInt(headers[1]);
        let status = parseInt(headers[2]);

        let extDataMap = new Map();
        if (extData !== null || extData !== "") {
            let entryValues = extData.split(NEWLINE);
            entryValues.forEach((entry) => {
                let entryStr = entry.split(":");
                extDataMap.set(entryStr[0], entryStr[1])
            });
        }

        let dataObject = JSON.parse(data);
        let rpcData = new RpcData(path, version, status, extDataMap, dataObject);
        return rpcData;
    }

    dateEncode(key, value) {
        if (value === undefined) return value;
        if (typeof key !== 'string') return value;  // 对数组进行过滤
        if (value instanceof Date || key === "createDateTime" || key.indexOf('Date') > 0 || key.indexOf('Time') > 0) {
            if (typeof value === "string") {
                let valueStr = value;
                if (valueStr.length < 19) return value;
                let formatValue = value.replace("T", " ");
                return formatValue.substring(0, 19);
            }
        }
        return value;
    }
}